package xymt.novaway.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import xymt.novaway.common.core.constant.CacheNames;
import xymt.novaway.common.core.constant.SystemConstants;
import xymt.novaway.common.core.domain.model.LoginUser;
import xymt.novaway.common.core.utils.StreamUtils;
import xymt.novaway.common.core.utils.StringUtils;
import xymt.novaway.common.satoken.utils.LoginHelper;
import xymt.novaway.system.domain.SysDept;
import xymt.novaway.system.domain.SysRoleDept;
import xymt.novaway.system.domain.SysRoleDataScopeConfig;
import xymt.novaway.system.mapper.SysDeptMapper;
import xymt.novaway.system.mapper.SysRoleDeptMapper;
import xymt.novaway.system.mapper.SysRoleDataScopeConfigMapper;
import xymt.novaway.system.service.ISysDataScopeService;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * 数据权限 实现
 * <p>
 * 注意: 此Service内不允许调用标注`数据权限`注解的方法
 * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题
 *
 * @author NovaWay
 */
@RequiredArgsConstructor
@Service("sdss")
@Slf4j
public class SysDataScopeServiceImpl implements ISysDataScopeService {

    /**
     * 最大权限规则数量限制
     */
    private static final int MAX_PERMISSION_RULES = 20;
    
    /**
     * 最大SQL条件长度限制（字符数）
     */
    private static final int MAX_SQL_LENGTH = 2000;
    
    /**
     * 权限规则性能告警阈值（毫秒）
     */
    private static final long PERFORMANCE_WARN_THRESHOLD_MS = 100;

    private final SysRoleDeptMapper roleDeptMapper;
    private final SysDeptMapper deptMapper;
    private final SysRoleDataScopeConfigMapper roleDataScopeConfigMapper;

    /**
     * 获取角色自定义权限
     *
     * @param roleId 角色Id
     * @return 部门Id组
     */
    @Cacheable(cacheNames = CacheNames.SYS_ROLE_CUSTOM, key = "#roleId", condition = "#roleId != null")
    @Override
    public String getRoleCustom(Long roleId) {
        if (ObjectUtil.isNull(roleId)) {
            return "-1";
        }
        List<SysRoleDept> list = roleDeptMapper.selectList(
            new LambdaQueryWrapper<SysRoleDept>()
                .select(SysRoleDept::getDeptId)
                .eq(SysRoleDept::getRoleId, roleId));
        if (CollUtil.isNotEmpty(list)) {
            return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId()));
        }
        return "-1";
    }

    /**
     * 获取部门及以下权限
     *
     * @param deptId 部门Id
     * @return 部门Id组
     */
    @Cacheable(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId", condition = "#deptId != null")
    @Override
    public String getDeptAndChild(Long deptId) {
        if (ObjectUtil.isNull(deptId)) {
            return "-1";
        }
        List<SysDept> deptList = deptMapper.selectListByParentId(deptId);
        List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
        ids.add(deptId);
        if (CollUtil.isNotEmpty(ids)) {
            return StreamUtils.join(ids, Convert::toStr);
        }
        return "-1";
    }

    /**
     * 获取角色数据权限过滤条件
     *
     * @param roleId 角色ID
     * @param fieldName 字段名
     * @param permission 权限标识
     * @return SQL过滤条件
     */
    @Cacheable(cacheNames = CacheNames.SYS_ROLE_DATA_SCOPE_CONFIG, 
               key = "#roleId + '_' + #fieldName + '_' + (#permission != null ? #permission : 'null')", 
               condition = "#roleId != null && #fieldName != null")
    @Override
    public String getRoleDataScopeFilter(Long roleId, String fieldName, String permission) {
        // 性能监控：记录开始时间
        long startTime = System.currentTimeMillis();
        
        if (ObjectUtil.isNull(roleId) || StringUtils.isBlank(fieldName)) {
            return "1 = 0";
        }
        
        log.debug("获取角色数据权限过滤条件: roleId={}, fieldName={}, permission={}", roleId, fieldName, permission);
        
        // 查询该角色、字段和权限的配置
        LambdaQueryWrapper<SysRoleDataScopeConfig> queryWrapper = new LambdaQueryWrapper<SysRoleDataScopeConfig>()
                .eq(SysRoleDataScopeConfig::getRoleId, roleId)
                .eq(SysRoleDataScopeConfig::getFilterField, fieldName)
                .eq(SysRoleDataScopeConfig::getEnableStatus, "0"); // 启用状态
        
        // 添加权限标识条件（如果提供）
        if (StringUtils.isNotBlank(permission)) {
            queryWrapper.eq(SysRoleDataScopeConfig::getPermission, permission);
        }
        
        List<SysRoleDataScopeConfig> configs = roleDataScopeConfigMapper.selectList(queryWrapper);
        
        if (CollUtil.isEmpty(configs)) {
            log.debug("未找到匹配的权限配置: roleId={}, fieldName={}, permission={}", roleId, fieldName, permission);
            return "1 = 0"; // 无权限配置时拒绝访问
        }
        
        // 权限规则复杂度控制：检查规则数量
        if (configs.size() > MAX_PERMISSION_RULES) {
            log.warn("角色{}的权限规则过多: {}个，超过限制{}个，拒绝访问", 
                     roleId, configs.size(), MAX_PERMISSION_RULES);
            return "1 = 0";
        }
        
        log.debug("找到{}个权限配置", configs.size());
        
        // 权限规则优先级排序：按菜单ID和ID排序，确保权限应用的一致性
        configs.sort((c1, c2) -> {
            // 首先按菜单ID排序
            int menuCompare = Long.compare(
                ObjectUtil.defaultIfNull(c1.getMenuId(), 0L), 
                ObjectUtil.defaultIfNull(c2.getMenuId(), 0L)
            );
            if (menuCompare != 0) {
                return menuCompare;
            }
            // 然后按ID排序（ID通常反映创建顺序）
            return Long.compare(
                ObjectUtil.defaultIfNull(c1.getId(), 0L), 
                ObjectUtil.defaultIfNull(c2.getId(), 0L)
            );
        });
        
        // 构建SQL过滤条件
        StringBuilder sqlBuilder = new StringBuilder();
        for (int i = 0; i < configs.size(); i++) {
            SysRoleDataScopeConfig config = configs.get(i);
            if (i > 0) {
                sqlBuilder.append(" OR ");
            }
            String condition = buildFilterCondition(fieldName, config);
            sqlBuilder.append(condition);
            log.debug("权限配置{}: filterType={}, filterValues={}, 生成条件: {}", 
                     i + 1, config.getFilterType(), config.getFilterValues(), condition);
        }
        
        String result = sqlBuilder.toString();
        
        // SQL长度控制：检查生成的SQL条件长度
        if (result.length() > MAX_SQL_LENGTH) {
            log.warn("角色{}生成的权限SQL过长: {}字符，超过限制{}字符，拒绝访问", 
                     roleId, result.length(), MAX_SQL_LENGTH);
            return "1 = 0";
        }
        
        // 性能监控：记录处理时间
        long processingTime = System.currentTimeMillis() - startTime;
        if (processingTime > PERFORMANCE_WARN_THRESHOLD_MS) {
            log.warn("权限规则处理性能告警: roleId={}, fieldName={}, permission={}, 处理时间={}ms", 
                     roleId, fieldName, permission, processingTime);
        } else {
            log.debug("权限规则处理完成: 处理时间={}ms", processingTime);
        }
        
        log.debug("最终权限过滤条件: {}", result);
        return result;
    }

    /**
     * 根据配置构建过滤条件
     *
     * @param fieldName 字段名
     * @param config 权限配置
     * @return 过滤条件SQL
     */
    private String buildFilterCondition(String fieldName, SysRoleDataScopeConfig config) {
        String filterType = StringUtils.isBlank(config.getFilterType()) ? "IN" : config.getFilterType().toUpperCase();
        String filterValues = config.getFilterValues();
        
        if (StringUtils.isBlank(filterValues)) {
            return "1 = 0";
        }
        
        // 处理动态变量替换
        filterValues = replaceDynamicVariables(filterValues);
        
        switch (filterType) {
            case "IN":
                // 解析JSON数组形式的值
                try {
                    List<String> valueList = JSONUtil.toList(filterValues, String.class);
                    if (CollUtil.isEmpty(valueList)) {
                        return "1 = 0";
                    }
                    String values = valueList.stream()
                        .map(v -> "'" + v.replace("'", "''") + "'") // SQL注入防护
                        .reduce((a, b) -> a + "," + b)
                        .orElse("");
                    return fieldName + " IN (" + values + ")";
                } catch (Exception e) {
                    // 如果不是JSON格式，按逗号分隔处理
                    String[] valueArray = filterValues.split(",");
                    String values = String.join(",", 
                        StreamUtils.toList(List.of(valueArray), v -> "'" + v.trim().replace("'", "''") + "'"));
                    return fieldName + " IN (" + values + ")";
                }
            case "EQ":
                return fieldName + " = '" + filterValues.replace("'", "''") + "'";
            case "LIKE":
                return fieldName + " LIKE '%" + filterValues.replace("'", "''") + "%'";
            case "NOT_IN":
                try {
                    List<String> valueList = JSONUtil.toList(filterValues, String.class);
                    if (CollUtil.isEmpty(valueList)) {
                        return "1 = 1";
                    }
                    String values = valueList.stream()
                        .map(v -> "'" + v.replace("'", "''") + "'")
                        .reduce((a, b) -> a + "," + b)
                        .orElse("");
                    return fieldName + " NOT IN (" + values + ")";
                } catch (Exception e) {
                    String[] valueArray = filterValues.split(",");
                    String values = String.join(",", 
                        StreamUtils.toList(List.of(valueArray), v -> "'" + v.trim().replace("'", "''") + "'"));
                    return fieldName + " NOT IN (" + values + ")";
                }
            default:
                return "1 = 0"; // 不支持的过滤类型，拒绝访问
        }
    }

    /**
     * 清理角色数据权限配置缓存
     * <p>
     * 当权限配置发生变更时，清理相关的缓存以确保数据一致性
     * 由于缓存键包含roleId、fieldName和permission，需要清理整个缓存空间
     * </p>
     *
     * @param roleId 角色ID
     */
    @org.springframework.cache.annotation.CacheEvict(
        cacheNames = CacheNames.SYS_ROLE_DATA_SCOPE_CONFIG, 
        allEntries = true
    )
    public void evictRoleDataScopeCache(Long roleId) {
        log.info("清理角色{}相关的数据权限配置缓存（清理整个缓存空间）", roleId);
    }
    
    /**
     * 清理所有数据权限配置缓存
     */
    @org.springframework.cache.annotation.CacheEvict(
        cacheNames = CacheNames.SYS_ROLE_DATA_SCOPE_CONFIG, 
        allEntries = true
    )
    public void evictAllDataScopeCache() {
        log.info("清理所有数据权限配置缓存");
    }

    /**
     * 替换动态变量
     * 支持 #{currentUserId}、#{currentDeptId} 等动态变量
     *
     * @param filterValues 原始过滤值
     * @return 替换后的过滤值
     */
    private String replaceDynamicVariables(String filterValues) {
        if (StringUtils.isBlank(filterValues) || !filterValues.contains("#{")) {
            return filterValues;
        }

        try {
            LoginUser user = LoginHelper.getLoginUser();
            if (user == null) {
                log.warn("无法获取当前登录用户信息，动态变量替换失败");
                return filterValues;
            }

            // 创建SpEL表达式解析上下文
            StandardEvaluationContext context = new StandardEvaluationContext();
            
            // 设置常用变量
            context.setVariable("currentUserId", user.getUserId());
            context.setVariable("currentDeptId", user.getDeptId());
            context.setVariable("currentUsername", user.getUsername());
            
            // 使用SpEL表达式解析器
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(filterValues, new TemplateParserContext());
            
            String result = expression.getValue(context, String.class);
            log.debug("动态变量替换: {} -> {}", filterValues, result);
            
            return result;
        } catch (Exception e) {
            log.warn("动态变量替换失败，原值: {}，错误: {}", filterValues, e.getMessage());
            // 降级处理：返回原值，避免影响正常功能
            return filterValues;
        }
    }

    /**
     * 根据权限标识动态获取角色数据权限过滤条件
     * <p>
     * 此方法实现了完全动态的数据权限过滤：
     * 1. 根据roleId和permission查询所有相关的过滤字段配置
     * 2. 为每个字段生成对应的SQL过滤条件
     * 3. 用AND连接所有条件形成最终的过滤条件
     * </p>
     *
     * @param roleId 角色ID
     * @param permission 权限标识
     * @return 组合的SQL过滤条件
     */
    @Override
    @Cacheable(cacheNames = CacheNames.SYS_ROLE_DATA_SCOPE_CONFIG,
               key = "#roleId + '_permission_' + #permission", 
               condition = "#roleId != null && #permission != null")
    public String getRoleDataScopeFilterByPermission(Long roleId, String permission) {
        long startTime = System.currentTimeMillis();
        
        if (roleId == null || StringUtils.isBlank(permission)) {
            log.debug("角色ID或权限标识为空，返回默认条件");
            return "";
        }
        
        log.debug("开始为角色{}和权限{}动态生成数据权限过滤条件", roleId, permission);
        
        try {
            // 查询该角色下指定权限的所有过滤配置
            LambdaQueryWrapper<SysRoleDataScopeConfig> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(SysRoleDataScopeConfig::getRoleId, roleId)
                       .eq(SysRoleDataScopeConfig::getPermission, permission)
                       .eq(SysRoleDataScopeConfig::getEnableStatus, SystemConstants.NORMAL)
                       .orderByAsc(SysRoleDataScopeConfig::getMenuId)
                       .orderByAsc(SysRoleDataScopeConfig::getId);
            
            List<SysRoleDataScopeConfig> configs = roleDataScopeConfigMapper.selectList(queryWrapper);
            
            if (CollUtil.isEmpty(configs)) {
                log.debug("角色{}在权限{}下未找到数据权限配置", roleId, permission);
                return "";
            }
            
            // 复杂度控制
            if (configs.size() > MAX_PERMISSION_RULES) {
                log.warn("角色{}的权限{}配置数量过多: {}个，超过限制{}个，拒绝访问", 
                         roleId, permission, configs.size(), MAX_PERMISSION_RULES);
                return "1 = 0";
            }
            
            log.debug("角色{}在权限{}下找到{}个数据权限配置", roleId, permission, configs.size());
            
            // 按字段分组，处理同字段多配置的情况
            Map<String, List<SysRoleDataScopeConfig>> configsByField = configs.stream()
                .collect(Collectors.groupingBy(SysRoleDataScopeConfig::getFilterField));
            
            List<String> fieldConditions = new ArrayList<>();
            
            // 为每个字段生成过滤条件
            for (Map.Entry<String, List<SysRoleDataScopeConfig>> entry : configsByField.entrySet()) {
                String fieldName = entry.getKey();
                List<SysRoleDataScopeConfig> fieldConfigs = entry.getValue();
                
                StringBuilder fieldCondition = new StringBuilder();
                
                // 同字段多配置用OR连接
                for (int i = 0; i < fieldConfigs.size(); i++) {
                    SysRoleDataScopeConfig config = fieldConfigs.get(i);
                    if (i > 0) {
                        fieldCondition.append(" OR ");
                    }
                    String condition = buildFilterCondition(fieldName, config);
                    fieldCondition.append(condition);
                    
                    log.debug("字段{}的第{}个配置: filterType={}, filterValues={}, 生成条件: {}", 
                             fieldName, i + 1, config.getFilterType(), config.getFilterValues(), condition);
                }
                
                // 将字段条件加入总条件列表
                if (fieldCondition.length() > 0) {
                    fieldConditions.add("(" + fieldCondition.toString() + ")");
                }
            }
            
            // 不同字段的条件用AND连接
            String result = String.join(" AND ", fieldConditions);
            
            // SQL长度控制
            if (result.length() > MAX_SQL_LENGTH) {
                log.warn("角色{}权限{}生成的SQL过长: {}字符，超过限制{}字符，拒绝访问", 
                         roleId, permission, result.length(), MAX_SQL_LENGTH);
                return "1 = 0";
            }
            
            // 性能监控
            long processingTime = System.currentTimeMillis() - startTime;
            if (processingTime > PERFORMANCE_WARN_THRESHOLD_MS) {
                log.warn("角色{}权限{}数据权限处理耗时过长: {}ms", roleId, permission, processingTime);
            }
            
            log.debug("角色{}权限{}最终生成条件: {}", roleId, permission, result);
            return result;
            
        } catch (Exception e) {
            long processingTime = System.currentTimeMillis() - startTime;
            log.error("角色{}权限{}数据权限处理异常，耗时: {}ms", roleId, permission, processingTime, e);
            return "1 = 0"; // 异常时拒绝访问
        }
    }

}
